/**
 * Copyright  2025, Hai Yue Xing He  ZHAO LIMIN
 * 
 * @author        ZHAO LIMIN
 * @version       1.0.1
 * @since         2025-04
 * 基于向量速度的抛物线轨迹工具类
 */
class HYXHProjectileMotionUtils {
    /**
     * 计算抛射体在给定时间的位置
     * @param initialPosition 初始位置 {x, y}
     * @param initialVelocity 初始速度向量 {x, y} (像素/秒)
     * @param gravity 重力加速度 (像素/秒²，默认980，相当于9.8m/s²假设1m=100像素)
     * @param time 经过的时间 (秒)
     * @returns 当前位置 {x, y}
     */
    static calculatePosition(
        initialPosition: { x: number; y: number },
        initialVelocity: { x: number; y: number },
        time: number,
        gravity: number = 980,
    ): { x: number; y: number } {
        return {
            x: initialPosition.x + initialVelocity.x * time,
            y: initialPosition.y + initialVelocity.y * time + 0.5 * gravity * time * time
        };
    }

    /**
     * 计算抛射体在给定时间的速度
     * @param initialVelocity 初始速度向量 {x, y}
     * @param gravity 重力加速度
     * @param time 经过的时间
     * @returns 当前速度 {x, y}
     */
    static calculateVelocity(
        initialVelocity: { x: number; y: number },        
        time: number,
        gravity: number = 980,
    ): { x: number; y: number } {
        return {
            x: initialVelocity.x, // x方向速度不变(忽略空气阻力)
            y: initialVelocity.y + gravity * time
        };
    }

    /**
     * 计算抛射体的最高点
     * @param initialPosition 初始位置
     * @param initialVelocity 初始速度
     * @param gravity 重力加速度
     * @returns 最高点信息 {position: {x, y}, time: 到达时间}
     */
    static calculatePeakPoint(
        initialPosition: { x: number; y: number },
        initialVelocity: { x: number; y: number },
        gravity: number = 980
    ): { position: { x: number; y: number }; time: number } {
        // 到达最高点时间 (当垂直速度为0时)
        const peakTime = -initialVelocity.y / gravity;
        const peakPosition = this.calculatePosition(initialPosition, initialVelocity, gravity, peakTime);
        
        return {
            position: peakPosition,
            time: peakTime
        };
    }

    /**
     * 计算抛射体的落地时间(返回y坐标等于初始y坐标的时间)
     * @param initialPosition 初始位置
     * @param initialVelocity 初始速度
     * @param gravity 重力加速度
     * @returns 落地时间 (秒)，如果没有落地则返回null
     */
    static calculateLandingTime(
        initialPosition: { x: number; y: number },
        initialVelocity: { x: number; y: number },
        gravity: number = 980
    ): number | null {
        // 解二次方程: 0.5 * gravity * t² + initialVelocity.y * t = 0
        // 即 t * (0.5 * gravity * t + initialVelocity.y) = 0
        // 解为 t=0(初始时刻) 和 t=-2*initialVelocity.y/gravity
        
        const landingTime = -2 * initialVelocity.y / gravity;
        return landingTime > 0 ? landingTime : null;
    }

    /**
     * 计算抛射体的射程(水平距离)
     * @param initialPosition 初始位置
     * @param initialVelocity 初始速度
     * @param gravity 重力加速度
     * @returns 射程 (像素)
     */
    static calculateRange(
        initialPosition: { x: number; y: number },
        initialVelocity: { x: number; y: number },
        gravity: number = 980
    ): number {
        const landingTime = this.calculateLandingTime(initialPosition, initialVelocity, gravity);
        if (landingTime === null) return 0;
        
        const landingPosition = this.calculatePosition(initialPosition, initialVelocity, gravity, landingTime);
        return landingPosition.x - initialPosition.x;
    }

    /**
     * 生成抛射体轨迹点
     * @param initialPosition 初始位置
     * @param initialVelocity 初始速度
     * @param gravity 重力加速度
     * @param duration 总持续时间 (秒)
     * @param pointCount 点的数量
     * @returns 轨迹点数组
     */
    static generateTrajectory(
        initialPosition: { x: number; y: number },
        initialVelocity: { x: number; y: number },
        duration: number,
        gravity: number = 980,
        pointCount: number = 30
    ): Array<{ x: number; y: number; time: number }> {
        const points: Array<{ x: number; y: number; time: number }> = [];
        const landingTime = this.calculateLandingTime(initialPosition, initialVelocity, gravity);
        const endTime = landingTime !== null ? Math.min(duration, landingTime) : duration;

        for (let i = 0; i <= pointCount; i++) {
            const time = (i / pointCount) * endTime;
            const position = this.calculatePosition(initialPosition, initialVelocity, time, gravity);
            points.push({ ...position, time });
        }

        return points;
    }

    /**
     * 根据目标点和速度大小计算最佳发射角度
     * @param startPosition 起始位置
     * @param targetPosition 目标位置
     * @param speed 速度大小 (像素/秒)
     * @param gravity 重力加速度
     * @returns 最佳发射角度 (弧度) 数组，可能有0-2个解
     */
    static calculateLaunchAngles(
        startPosition: { x: number; y: number },
        targetPosition: { x: number; y: number },
        speed: number,
        gravity: number = 980
    ): number[] {
        const dx = targetPosition.x - startPosition.x;
        const dy = targetPosition.y - startPosition.y;
        
        // 特殊情况处理
        if (dx === 0) {
            // 垂直发射情况
            const discriminant = speed * speed - 2 * gravity * dy;
            if (discriminant < 0) return [];
            
            const angle1 = Math.PI / 2;
            // 如果有两个解，第二个是向下发射
            const angle2 = discriminant > 0 ? -Math.PI / 2 : undefined;
            return angle2 !== undefined ? [angle1, angle2] : [angle1];
        }
        
        // 一般情况解
        const discriminant = Math.pow(speed, 4) - gravity * (gravity * dx * dx + 2 * dy * speed * speed);
        if (discriminant < 0) return [];
        
        const sqrtDiscriminant = Math.sqrt(discriminant);
        const angle1 = Math.atan((speed * speed + sqrtDiscriminant) / (gravity * dx));
        const angle2 = Math.atan((speed * speed - sqrtDiscriminant) / (gravity * dx));
        
        return [angle1, angle2].filter(a => !isNaN(a));
    }
}